package client;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;

import server.Middleware;

import config.ConfigurationException;
import config.Log;

import core.IClient;
import core.IMessageReader;
import core.IMessageWriter;
import core.LerConsole;
import core.MessageType;
import core.Receive;
import core.Sender;
import core.Util;


public class Cliente implements IMessageReader, IMessageWriter, IClient{
	
	private Vector<String> servers = new Vector<String>();
	//map contendo nome do servidor e lista dos nomes de seus recursos
	private Set<String> recursosTotais = new HashSet<String>();
	// Contem informações sobre um recurso que o cliente solicitou
	private Hashtable<String, String> recursosUtilizados = new Hashtable<String, String>();
	
	private String endereco;
	private String canal;
	private Receive receive;
	
	private int numServerResponse = 0;
	
	public Cliente(String endereco, String canal, Vector<String> servers) {
		this.endereco = endereco;
		this.canal = canal;
		this.servers = servers;
		
		this.receive = new Receive(this);
		messageSystemInit(endereco, canal);
		   
		solicitarRecursos();
		menu();
	}
	
	public void menu() {
		int escolha = 0;
		
		System.out.println("\nEscolha uma opção:");
		System.out.println("    [1] Atualizar recursos disponíveis");
		System.out.println("    [2] Solicitar um recurso disponível");
		System.out.println("    [3] Sair\n");
		System.out.println("escolha uma das opções...");
		
		while (escolha != 3) {
			
			escolha = LerConsole.readInteger();
			
			switch (escolha) {
			case 1:
				solicitarRecursos();
				System.out.println("Solicitando recursos... Aguarde!");
				break;
			
			case 2:
				System.out.println("Nome do recurso: ");
				String nomeRecurso = LerConsole.readString();
				System.out.println("Tempo de uso do recurso: ");
				String tempoRecurso = LerConsole.readString();
				pedirRecurso(nomeRecurso, tempoRecurso);
				break;

			default:
				break;
			}
		}
	}
	
	private void messageSystemInit(String endereco, String canal){
		try {
			Log.getInstance(Middleware.class).getLogger().info(
					"Iniciando leitura e escrita em JMS - " + Util.getTime()	
			);
			
			receive.inicia(endereco, canal);
			Thread.sleep(2000);
		}catch (InterruptedException e) {
			e.printStackTrace();
		
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void readMessage(String message) {
		try {
			Log.getInstance(Cliente.class).getLogger().info(
					"Lendo mensagem em: " + this.canal + "; " +
							"mensagem: " + message + " - " + Util.getTime()	
			);
		} catch (ConfigurationException e) {
			e.printStackTrace();
		}
		
		String[] messages = message.split("\\" + MessageType.SPLIT);
		if (messages.length > 1){
			
			if (MessageType.LISTAGEM_RECURSOS.toString().equals(messages[0])){
				for (int i = 2; i < messages.length; i++){
					recursosTotais.add(messages[i]);
				}
				numServerResponse++; // Soma servidores que responderam
				if (numServerResponse == servers.size()) {
					System.out.println("\nListagem de recursos:");
					for (String recurso : recursosTotais) {
						System.out.println("\t" + recurso);
					}
					numServerResponse = 0;
				}
			}
			
			/**
			 * Estilo de mensagem para as três comparações abaixo:
			 *  messages[0]: notificação
			 *  messages[1]: servidor que enviou
			 *  messages[2]: nome do recurso
			 *  
			 *  Casos para verificar (testes de permutas):
			 *  	1º) ServidorA	|	2º) ServidorB
			 *  			S		|		F
			 *  			S		|		S
			 *  			F		|		S
			 *  			F		|		F
			 */
			else if (MessageType.LIBERACAO_RECURSO.toString().equals(messages[0])) {
				
				// Verifica se o cliente ainda precisa do recurso
				if (recursosUtilizados.containsKey(messages[2])) {
					
					if (recursosUtilizados.get(messages[2]) == "nada") {
						recursosUtilizados.put(messages[2], "usando");
					}
					
					else if (recursosUtilizados.get(messages[2]) == "espera") {
						recursosUtilizados.put(messages[2], "usando");
					}
					
					else {
						//tipo da mensagem: recusar_recurso|client15|t
						String msg = MessageType.RECUSAR_RECURSO.toString() 
							+ MessageType.SPLIT + this.canal + MessageType.SPLIT + messages[2];
						writeMessage(this.endereco, messages[1], msg);
					}
				}
				// Cliente já utilizou o recurso. Não precisa mais
				else {
					String msg = MessageType.RECUSAR_RECURSO.toString() 
					+ MessageType.SPLIT + this.canal + MessageType.SPLIT + messages[2];
					writeMessage(this.endereco, messages[1], msg);
				}
				
			}
			
			else if (MessageType.FINALIZA_RECURSO.toString().equals(messages[0])) {
				if (recursosUtilizados.containsKey(messages[2])) {
					recursosUtilizados.remove(messages[2]);
				}
			}
			
			else if (MessageType.ESPERA_RECURSO.toString().equals(messages[0])) {
				if (recursosUtilizados.containsKey(messages[2])) {
					if (recursosUtilizados.get(messages[2]) == "nada") {
						recursosUtilizados.put(messages[2], "espera");
					}
					else if (recursosUtilizados.get(messages[2]) == "usando") {
						String msg = MessageType.RECUSAR_RECURSO.toString() 
						+ MessageType.SPLIT + this.canal + MessageType.SPLIT + messages[2];
						writeMessage(this.endereco, messages[2], msg);
					}
				}
			}
		}
	}

	@Override
	public void writeMessage(String endereco, String canal, String message) {
		try {
			Log.getInstance(Cliente.class).getLogger().info(
					"Escrevendo mensagem de: " + this.canal + " para " + canal + "; " +
							"mensagem: " + message + " - " + Util.getTime()	
			);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		Sender sender = new Sender();
		sender.inicia(endereco, canal);
		sender.sendMessage(message);
	}
	
	@Override
	/**
	 * Exemplo de mensagem enviada: listagem_recursos|client1 
	 */
	public void solicitarRecursos() {
		for (String server : this.servers){
			String mensagem = MessageType.LISTAGEM_RECURSOS.toString() + MessageType.SPLIT + this.canal;
			writeMessage(this.endereco, server, mensagem);
		}
	}

	@Override
	/**
	 * Exemplo de mensagem enviada: solicitacao_recurso|client1|impressora|20 
	 */
	public void pedirRecurso(String recursoNome, String tempoRecurso) {
		for (String server : this.servers) {
			String mensagem = MessageType.SOLICITACAO_RECURSO.toString()
					+ MessageType.SPLIT + this.canal + MessageType.SPLIT
					+ recursoNome + MessageType.SPLIT + tempoRecurso;
			writeMessage(this.endereco, server, mensagem);
		}
		// Insere o recurso em um hash de notificação. Não está usando ainda
		recursosUtilizados.put(recursoNome, "nada");
	}
	
}